协方差(Covariance)和相关系数(Correlation)是衡量两个变量关系的基础统计量。
在金融领域,这些指标帮助我们:
总体协方差:
\[ \text{Cov}(X, Y) = E[(X - \mu_X)(Y - \mu_Y)] \]
样本协方差:
\[ \text{Cov}(X, Y) = \frac{1}{n-1}\sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y}) \]
直觉理解:协方差衡量两个变量的「同步偏离」程度
\[ \rho_{XY} = \frac{\text{Cov}(X, Y)}{\sigma_X \sigma_Y} = \frac{\sum(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum(x_i - \bar{x})^2}\sqrt{\sum(y_i - \bar{y})^2}} \]
核心性质:
| 特性 | 协方差 | 相关系数 |
|---|---|---|
| 量纲 | 有单位(如%²) | 无量纲 |
| 范围 | \((-\infty, +\infty)\) | \([-1, 1]\) |
| 标准化 | 否 | 是 |
| 可比性 | 不同资产对不可比 | 所有资产对可比 |
| 应用 | 计算组合方差 | 衡量关系强度 |
以下代码与教学平台任务要求完全一致:
以下代码与教学平台任务要求完全一致:
# 注:相关分析sales.csv数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import pandas as pd
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
import numpy as np # 导入NumPy数值计算库
# 读取数据
data = pd.read_csv('相关分析sales.csv')
# 计算皮尔逊相关系数
corr =
# 计算相关性矩阵
corr_matrix = data.corr()
# 绘制热力图
fig, ax = plt.subplots()
im = ax.imshow(corr_matrix, cmap='YlGnBu') # 显示热力图矩阵
# 显示相关系数的值
for i in range(corr_matrix.shape[0]):
for j in range(corr_matrix.shape[1]): # 遍历range(corr_matrix.shape[1])中的每个j
text = ax.text(j, i, round(corr_matrix.iloc[i, j],2), # 按位置索引提取显示相关系数的值
ha="center", va="center", color="black") # 设置标注文本的水平和垂直对齐方式
# 设置坐标轴标签和标题
ax.set_xticks(np.arange(len(corr_matrix.columns)))
ax.set_yticks(np.arange(len(corr_matrix.columns))) # 设置Y轴刻度标签
ax.set_xticklabels(corr_matrix.columns) # 设置X轴刻度标签
ax.set_yticklabels(corr_matrix.columns) # 设置Y轴刻度标签
ax.set_title("Correlation Heatmap") # 设置图表标题
# 在图形旁添加颜色条
cbar = ax.figure.colorbar(im, ax=ax)
# 显示图形
plt.savefig("热力图.png")
# 输出结果
print('Pearson correlation coefficient: ', corr)
if abs(corr) >= 0.7: # 条件判断:abs(corr) >= 0.7
print('存在显著的线性相关性') # 输出存在显著的线性相关性
else: # 不满足以上条件时
print('不存在显著的线性相关性') # 输出不存在显著的线性相关性
#任务二
import numpy as np
from scipy import stats # 导入SciPy科学计算库
import pandas as pd # 导入Pandas数据分析库
import statsmodels.api as sm # 导入统计建模库
import statsmodels.formula.api as smf # 导入统计建模库
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
from statsmodels.stats.multicomp import pairwise_tukeyhsd # 导入统计建模库
from statsmodels.graphics.api import interaction_plot # 导入统计建模库
from matplotlib.font_manager import FontProperties # 导入Matplotlib库
myfont= # 设置中文字体属性
# 从CSV文件读取数据存入sale_points
sale_points = pd.read_csv(u'https://huoran.oss-cn-shenzhen.aliyuncs.com/20221116/csv/1592817699758039040.csv',encoding = "gbk")
sale_points['market'] = sale_points['market'].astype('category') # 转换数据类型
# 定义列表sale_points['market'].cat.categories
sale_points['market'].cat.categories=['market 1', 'market 2', 'market 3']
sale_points['warranty'] = sale_points['warranty'].astype('category') # 转换数据类型
# 定义列表sale_points['warranty'].cat.categories
sale_points['warranty'].cat.categories=['1 years', '3 years']
print(sale_points.head()) # 输出前几行数据
formula = 'sales ~ points + C(market) * C(warranty)' # 定义模型公式:sales ~ points + C(market) * C(warranty)
sale_points_anova_cov_est = smf.ols(formula, data = sale_points).fit() # dc_sales_est 是一个模型对象
print(sale_points_anova_cov_est.summary()) # 输出合计值import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 创建多资产收益率数据(模拟500个交易日)
np.random.seed(42)
n_days = 500
# 各资产的期望日收益率
mean_returns = [0.001, 0.0008, 0.0012, 0.0005]
# 设定真实的协方差矩阵
cov_matrix_true = np.array([
[0.0004, 0.0002, 0.00015, 0.0001],
[0.0002, 0.0003, 0.0001, 0.00005],
[0.00015, 0.0001, 0.00035, 0.00008],
[0.0001, 0.00005, 0.00008, 0.0002]
])
# 生成多元正态分布数据
returns = np.random.multivariate_normal(mean_returns, cov_matrix_true, n_days)
# 转换为DataFrame
assets = ['贵州茅台', '五粮液', '招商银行', '中国平安']
df_returns = pd.DataFrame(returns, columns=assets)
print('收益率数据(前10行):')
print(df_returns.head(10))收益率数据(前10行):
贵州茅台 五粮液 招商银行 中国平安
0 -0.021316 0.007675 -0.001451 -0.000617
1 -0.006528 0.016661 0.013422 -0.009431
2 0.016949 0.004799 -0.001382 0.004445
3 -0.004128 -0.029601 0.015739 0.019084
4 0.033404 0.001141 0.007167 0.008088
5 -0.016207 -0.029359 -0.014025 -0.017959
6 0.012169 0.004621 0.001933 0.018252
7 -0.001172 0.015955 0.009460 0.022482
8 0.001602 -0.010140 0.018858 -0.012143
9 -0.010412 -0.021935 0.018074 0.019635
协方差矩阵:
贵州茅台 五粮液 招商银行 中国平安
贵州茅台 0.000361 0.000171 0.000115 0.000072
五粮液 0.000171 0.000289 0.000077 0.000033
招商银行 0.000115 0.000077 0.000329 0.000055
中国平安 0.000072 0.000033 0.000055 0.000179
相关系数矩阵:
贵州茅台 五粮液 招商银行 中国平安
贵州茅台 1.0000 0.5276 0.3322 0.2823
五粮液 0.5276 1.0000 0.2494 0.1438
招商银行 0.3322 0.2494 1.0000 0.2283
中国平安 0.2823 0.1438 0.2283 1.0000
# 提取上三角矩阵(排除对角线),避免重复
mask = np.triu(np.ones_like(corr_matrix, dtype=bool), k=1)
corr_upper = corr_matrix.where(mask)
# 找最大和最小相关系数
max_corr = corr_upper.max().max()
min_corr = corr_upper.min().min()
max_pair = corr_upper.stack().idxmax()
min_pair = corr_upper.stack().idxmin()
print(f'最高相关性: {max_pair} = {max_corr:.4f}')
print(f'最低相关性: {min_pair} = {min_corr:.4f}')最高相关性: ('贵州茅台', '五粮液') = 0.5276
最低相关性: ('五粮液', '中国平安') = 0.1438
| 方案 | 特点 | 适用场景 |
|---|---|---|
RdYlGn |
红-黄-绿,中心为0 | 一般相关性 |
coolwarm |
蓝-白-红,中心为0 | 科学出版 |
viridis |
紫-黄,感知均匀 | 色盲友好 |
RdBu |
红-蓝,发散 | 正负对比明显 |
组合方差公式(矩阵形式):
\[ \sigma_p^2 = \mathbf{w}^T \Sigma \mathbf{w} \]
其中:
当资产间相关系数 \(\rho_{ij} < 1\) 时:
\[ \sigma_p^2 < \sum_{i=1}^n w_i^2 \sigma_i^2 \]
含义:组合方差小于各资产方差的加权平均,实现风险分散。
这正是投资组合理论的核心价值!
assets_list = ['贵州茅台', '五粮液', '招商银行', '中国平安']
n_assets = len(assets_list)
# 日协方差 → 年化协方差(×252个交易日)
cov_annual = cov_matrix * 252
# 生成1000个随机组合
np.random.seed(42)
n_portfolios = 1000
weights_list = []
returns_list = []
risks_list = []
for _ in range(n_portfolios):
weights = np.random.random(n_assets)
weights = weights / np.sum(weights) # 归一化,权重和为1
# 组合期望收益(年化)
portfolio_return = np.sum(df_returns.mean() * weights) * 252
# 组合方差 = w^T * Σ * w
portfolio_variance = np.dot(weights.T, np.dot(cov_annual, weights))
portfolio_std = np.sqrt(portfolio_variance)
weights_list.append(weights)
returns_list.append(portfolio_return)
risks_list.append(portfolio_std)
df_portfolios = pd.DataFrame({
'收益率': returns_list,
'风险(标准差)': risks_list
})plt.figure(figsize=(12, 8))
scatter = plt.scatter(
df_portfolios['风险(标准差)'], df_portfolios['收益率'],
c=df_portfolios['收益率'] / df_portfolios['风险(标准差)'],
cmap='viridis', alpha=0.6, s=50
)
plt.colorbar(scatter, label='夏普比率')
# 标记单个资产
for i, asset in enumerate(assets_list):
asset_return = df_returns[asset].mean() * 252
asset_risk = np.sqrt(cov_annual.iloc[i, i])
plt.scatter(asset_risk, asset_return, s=200, marker='*',
color='red', edgecolors='black', linewidths=1.5)
plt.text(asset_risk, asset_return, f' {asset}', fontsize=10)
plt.xlabel('年化风险(标准差)', fontsize=12)
plt.ylabel('年化收益率', fontsize=12)
plt.title('投资组合:风险与收益的关系', fontsize=16, fontweight='bold')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()最优组合权重(最大夏普比率):
贵州茅台: 1.73%
五粮液: 71.54%
招商银行: 8.59%
中国平安: 18.14%
# 计算贵州茅台和五粮液的60日滚动相关系数
window = 60
rolling_corr = df_returns['贵州茅台'].rolling(window).corr(df_returns['五粮液'])
plt.figure(figsize=(14, 6))
plt.plot(df_returns.index, rolling_corr, linewidth=2, color='#2E86AB')
# 参考线
plt.axhline(y=0, color='black', linestyle='-', linewidth=0.5)
plt.axhline(y=rolling_corr.mean(), color='red', linestyle='--',
linewidth=2, label=f'均值={rolling_corr.mean():.4f}')
# 填充正负相关区域
plt.fill_between(df_returns.index, rolling_corr, 0,
where=(rolling_corr >= 0), alpha=0.3, color='green', label='正相关期')
plt.fill_between(df_returns.index, rolling_corr, 0,
where=(rolling_corr < 0), alpha=0.3, color='red', label='负相关期')
plt.title('滚动相关系数(60日窗口)', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('相关系数', fontsize=12)
plt.legend(fontsize=11, loc='upper left')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()print('相关系数统计:')
print(f'均值: {rolling_corr.mean():.4f}')
print(f'标准差: {rolling_corr.std():.4f}')
print(f'最大值: {rolling_corr.max():.4f}')
print(f'最小值: {rolling_corr.min():.4f}')
# 识别相关性显著下降的时期
threshold = rolling_corr.mean() - 2 * rolling_corr.std()
low_corr_periods = rolling_corr[rolling_corr < threshold]
print(f'\n相关性异常低的时期({len(low_corr_periods)}天):')
print(low_corr_periods.head())相关系数统计:
均值: 0.5228
标准差: 0.1118
最大值: 0.6752
最小值: 0.2269
相关性异常低的时期(28天):
397 0.289964
398 0.298138
399 0.298487
401 0.279462
402 0.275398
dtype: float64
| 窗口 | 天数 | 特点 | 适用场景 |
|---|---|---|---|
| 短期 | 20日 | 捕捉快速变化,噪声大 | 短线交易 |
| 中期 | 60日 | 平衡灵敏度和稳定性 | 一般分析 |
| 长期 | 120日 | 平滑,识别长期关系 | 战略配置 |
industries_network = ['金融', '科技', '消费', '医药', '能源', '地产']
n_industries = len(industries_network)
# 构建行业相关系数矩阵
industry_corr = pd.DataFrame(
[[1.00, 0.30, 0.35, 0.25, 0.40, 0.60],
[0.30, 1.00, 0.55, 0.30, 0.25, 0.20],
[0.35, 0.55, 1.00, 0.35, 0.20, 0.25],
[0.25, 0.30, 0.35, 1.00, 0.15, 0.20],
[0.40, 0.25, 0.20, 0.15, 1.00, 0.35],
[0.60, 0.20, 0.25, 0.20, 0.35, 1.00]],
index=industries_network, columns=industries_network
)
print('行业相关系数矩阵:')
print(industry_corr)行业相关系数矩阵:
金融 科技 消费 医药 能源 地产
金融 1.00 0.30 0.35 0.25 0.40 0.60
科技 0.30 1.00 0.55 0.30 0.25 0.20
消费 0.35 0.55 1.00 0.35 0.20 0.25
医药 0.25 0.30 0.35 1.00 0.15 0.20
能源 0.40 0.25 0.20 0.15 1.00 0.35
地产 0.60 0.20 0.25 0.20 0.35 1.00
high_corr_pairs = []
for i in range(n_industries):
for j in range(i+1, n_industries):
corr_val = industry_corr.iloc[i, j]
if abs(corr_val) > 0.5:
high_corr_pairs.append((industries_network[i], industries_network[j], corr_val))
print('高度相关行业对(|ρ| > 0.5):')
for pair in sorted(high_corr_pairs, key=lambda x: abs(x[2]), reverse=True):
print(f'{pair[0]} - {pair[1]}: {pair[2]:.2f}')高度相关行业对(|ρ| > 0.5):
金融 - 地产: 0.60
科技 - 消费: 0.55
基于相关性的行业轮动原则:
实例:金融-地产相关系数0.60(同属周期板块),科技-消费相关系数0.55(消费升级主题)
偏相关系数 \(\rho_{XY|Z}\):控制变量 \(Z\) 之后,\(X\) 和 \(Y\) 的真实相关性。
金融应用:
from scipy.stats import pearsonr
from sklearn.linear_model import LinearRegression
np.random.seed(42)
n = 500
# 构造三变量数据
market = np.random.normal(0.001, 0.02, n)
industry_factor = np.random.normal(0, 0.01, n)
stock = 0.8 * market + 0.5 * industry_factor + np.random.normal(0, 0.015, n)
df_partial = pd.DataFrame({
'市场': market,
'个股': stock,
'行业因子': industry_factor
})
# 简单相关系数
corr_market_stock = df_partial['市场'].corr(df_partial['个股'])
corr_industry_stock = df_partial['行业因子'].corr(df_partial['个股'])
print('简单相关系数:')
print(f'市场-个股: {corr_market_stock:.4f}')
print(f'行业因子-个股: {corr_industry_stock:.4f}')简单相关系数:
市场-个股: 0.6652
行业因子-个股: 0.2234
# 个股对行业因子回归,取残差
model_stock = LinearRegression().fit(
df_partial[['行业因子']], df_partial['个股']
)
residual_stock = df_partial['个股'] - model_stock.predict(df_partial[['行业因子']])
# 市场对行业因子回归,取残差
model_market = LinearRegression().fit(
df_partial[['行业因子']], df_partial['市场']
)
residual_market = df_partial['市场'] - model_market.predict(df_partial[['行业因子']])
# 偏相关系数 = 残差的相关系数
partial_corr = np.corrcoef(residual_market, residual_stock)[0, 1]
print(f'偏相关系数(控制行业因子):')
print(f'市场-个股: {partial_corr:.4f}')
print(f'解释: 剔除行业影响后,市场与个股的真实相关性')偏相关系数(控制行业因子):
市场-个股: 0.7018
解释: 剔除行业影响后,市场与个股的真实相关性
[商业大数据分析与应用]